#!/usr/bin/perl -w

use strict;
use warnings;
use FindBin;
use Term::ReadLine;

my $Gworkdir = $FindBin::Bin;
unshift @INC, "$Gworkdir";
unshift @INC, "$Gworkdir/IssDump/blib/lib";
unshift @INC, "$Gworkdir/IssDump/blib/arch";

require DumpParse; DumpParse->import(":all");
require SymbolTable; SymbolTable->import(":all");
require IssDump; IssDump->import(":all");

print "Press Ctrl + C to exit\n";

source("$Gworkdir/memory.pl");

my $term = new Term::ReadLine 'ProgramName';
my $attribs = $term->Attribs;

my @allsubs = ();
foreach my $entry ( keys %main:: )
{
  push(@allsubs,$entry) if defined &{$entry};
}

$attribs->{completion_word} = \@allsubs;
$attribs->{attempted_completion_function} = sub {
  my ($text, $line, $start, $end) = @_;
  # If first word then username completion, else filename completion
  if (substr($line, 0, $start) =~ /^\s*$/) {
    return $term->completion_matches($text,
                                     $attribs->{list_completion_function});
  } else {
    return ();
  }
};
load_dump("good.dmp");
my $cmd = "";
select STDOUT; $| = 1;
while ( defined ($cmd = $term->readline('IssDump> ')) ) {
  my ($redirect, $oldout);
  
  $redirect = 0;
  if($cmd =~ m/(.*)>\s*([\w\.]+)$/) {
    $cmd = $1;
    $redirect = 1;
    open $oldout, ">&STDOUT" or die "Can't dup STDOUT: $!";
    open STDOUT, '>', $2 or die "Can't redirect STDOUT: $!";
    select STDOUT; $| = 1;
    warn "STDOUT redirects to $2\n";
  }
  eval($cmd);
  if($redirect == 1) {
    close STDOUT;
    open STDOUT, ">&", $oldout or die "Can't dup \$oldout: $!";
    select STDOUT; $| = 1;
    close $oldout;
  }
  warn $@ if $@;
}
# while(1) {
#   print "IssDump> ";
#   my $code=<STDIN>;
#   chomp $code;
#   eval($code);
#   warn $@ if $@;
# }
#Dmp_LoadDump("pdisk112_0_206.dmp");
#Dmp_PrintHeader();
# Sym_Load("20101500004F/mayinit.sym");
# printf "Knl_PageTableStack at %08x\n",SymbolTable::Sym_SymToAddr("Knl_PageTableStack");

sub source {
  my $path = shift;
  open (my $fh, "<", $path) or warn "Cannot open $path\n";
  return if(!$fh);

  my $content = "";
  while(<$fh>) {
    $content .= $_;
  }
  close $fh;
  eval($content);
  warn $@ if $@;

  @allsubs = ();
  for my $entry ( keys %main:: ) {
    push(@allsubs,$entry) if defined &{$entry};
  }
}

sub load_dump {
  my $path = shift;

  my $ret = Dmp_Load($path);
  if($ret != 0) {
    warn "Dump '$path' load failed\n";
  }
  if($ret == -1) {
    warn "File not found\n";
    return;
  }
  if($ret == -2) {
    warn "Invalid dump\n";
    return;
  }
  warn "Dump '$path' loaded\n";

  if( defined (my $level = get_level()) ) {
    load_sym( "$level/mayinit.sym" );
  } else {
    warn "Cannot figure out the level of the dump\n";
  }
}

sub load_sym {
  my $path = shift;

  if(!Sym_Load($path)) {
    warn "Load sym $path failed\n";
    return;
  }
  warn "Symbol $path loaded\n";
}

sub get_level {
  #my $anchor_addr = 0x1010c200;
  my $anchor_addr = 0x40086200;
  
  return undef if( Dmp_Iu32AtPpc($anchor_addr + 0x68) != 0x12345678 );
  
  #my $level = Dmp_StringAtPpc($anchor_addr + 0x5c, 12);
  my $level = "";
  for( my $i = 0; $i < 12; $i++ ) {
    $level .= chr Dmp_Iu8AtPpc( $anchor_addr + 0x5c + $i );
  }
  warn "Level in the anchor is $level\n";
  return undef if( $level !~ /^[\w\d]+$/ );
  return $level;
}
